package cn.st4rlight.util.exception;

import static cn.st4rlight.util.response.ResultCode.BAD_REQUEST;
import static cn.st4rlight.util.response.ResultCode.SERVICE_BUSY;

import java.util.List;
import java.util.Objects;

import org.apache.catalina.connector.ClientAbortException;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.ServletRequestBindingException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.servlet.NoHandlerFoundException;

import cn.st4rlight.util.response.Message;
import cn.st4rlight.util.response.ResultCode;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;

/**
 * Spring 全局异常处理器
 *
 * @author st4rlight <st4rlight@163.com>
 * Created on 2025-05-03
 */
@Slf4j
@RestControllerAdvice
public class ExceptionHandlers {

    // 兜底错误文案
    private static final String BASE_ERROR_MSG = "系统调用异常，请稍后重试";


    /**
     * 明确可以忽略的异常信息
     * 无需打印异常堆栈信息
     */
    @ExceptionHandler({ClientAbortException.class, HttpRequestMethodNotSupportedException.class,
        HttpMessageNotReadableException.class, MethodArgumentTypeMismatchException.class,
        BindException.class, NoHandlerFoundException.class, ServletRequestBindingException.class,
        HttpMediaTypeNotSupportedException.class, MultipartException.class
    })
    public Message<Void> handleIgnoreException(HttpServletRequest request, Exception exception) {
        return Message.error(BAD_REQUEST, "请求异常，请检查输入的参数是否正确");
    }

    /**
     * JSR 303
     */
    @ExceptionHandler({MethodArgumentNotValidException.class})
    public Message<Void> jsr303Exception(HttpServletRequest request, MethodArgumentNotValidException exception) {
        // 从exception中拿到BindingResult
        BindingResult bindingResult = exception.getBindingResult();
        List<FieldError> fieldErrors = bindingResult.getFieldErrors();
        Message<Void> resultMessage = Message.ok();

        if (CollectionUtils.isNotEmpty(fieldErrors)) {
            FieldError fieldError = fieldErrors.get(0);
            String errorMessage = StringUtils.trimToEmpty(fieldError.getDefaultMessage());
            log.warn("jsr303校验异常：{}", errorMessage);
            resultMessage = Message.error(ResultCode.PARAM_INVALID, errorMessage);
        } else {
            resultMessage = Message.error(ResultCode.PARAM_INVALID, "jsr303校验异常");
        }
        return resultMessage;
    }

    /**
     * 统一业务异常错误
     */
    @ExceptionHandler({BizException.class})
    public Message<Void> handleBizException(HttpServletRequest request, BizException bizException) {
        ResultCode resultCode = bizException.getResultCode();
        int code = Objects.nonNull(resultCode) ? resultCode.getCode() : BAD_REQUEST.getCode();
        return Message.error(code, bizException.getMessage());
    }

    /**
     * 系统异常
     */
    @ExceptionHandler({ServiceException.class})
    public Message<Void> handleException(HttpServletRequest request, ServiceException exception) {
        log.error("系统异常：{}", exception.getMessage(), exception);
        return Message.error(SERVICE_BUSY, exception.getMessage());
    }

    /**
     * 兜底异常
     */
    @ExceptionHandler({Throwable.class})
    public Message<Void> handleException(HttpServletRequest request, Throwable exception) {
        log.error("兜底异常：{}", exception.getMessage(), exception);
        return Message.error(SERVICE_BUSY, BASE_ERROR_MSG);
    }
}
